use tracing::{error, info, warn};

use bc_base::{self, BlockId, block::Block, transaction::Transaction};
use bc_p2p::network_api::P2PEventForApp;

use crate::BcManagerHandler;

pub async fn start_process_p2p_user_event_task(
    bc_manager: BcManagerHandler,
    shutdown_rx: tokio::sync::watch::Receiver<bool>,
    shutdown_tx: tokio::sync::watch::Sender<bool>,
) -> tokio::task::JoinHandle<()> {
    tokio::task::spawn(async move {
        let event_receiver_for_app = bc_manager.lock().await.take_event_receiver_for_app().await;
        process_p2p_user_event_task_entry(
            bc_manager,
            event_receiver_for_app,
            shutdown_rx,
            shutdown_tx,
        )
        .await;
    })
}

async fn process_p2p_user_event_task_entry(
    bc_manager: BcManagerHandler,
    mut event_receiver_for_app: tokio::sync::mpsc::Receiver<P2PEventForApp>,
    mut shutdown_rx: tokio::sync::watch::Receiver<bool>,
    shutdown_tx: tokio::sync::watch::Sender<bool>,
) {
    println!("🚀 Starting the P2P event loop task for APP...");
    bc_util::print_task_and_thread_id("P2P network user event task");

    loop {
        tokio::select! {
            _ = shutdown_rx.changed() => {
                if *shutdown_rx.borrow() {
                    info!("Shutdown signal received for the event for APP loop task.");
                    break;
                }
            },
            _ = tokio::signal::ctrl_c() => {
                println!("Received Ctrl+C signal, stopping blockchain core logic...");
                let _ = shutdown_tx.send(true);
            },

            event = event_receiver_for_app.recv() => {
                if let Some(event) = event {
                    do_process_event(event, &bc_manager).await;
                } else {
                    info!("Event for APP receiver channel closed.");
                    let _ = shutdown_tx.send(true);
                    break;
                }
            }
        }
    }
    println!("🛑 The P2P event loop task for APP stoped.");
}

async fn do_process_event(event: P2PEventForApp, bc_manager: &BcManagerHandler) {
    match event {
        P2PEventForApp::NewListenAddr(addr) => {
            println!("New listen address: {}", addr);
        }
        P2PEventForApp::PeerDiscovered(peer_id) => {
            println!("New peer discovered: {}", peer_id);
        }
        P2PEventForApp::PeerExpired(peer_id) => {
            println!("Peer expired: {}", peer_id);
        }

        P2PEventForApp::MessageReceived {
            topic,
            from_peer,
            data,
        } => {
            info!(
                "Message received from peer {}: about topic {}",
                from_peer, topic
            );

            if bc_manager.lock().await.is_block_topic(&topic).await {
                let block = Block::deserialize(&data);
                match block {
                    Ok(block) => {
                        let block_id = block.calc_block_id();
                        info!("Received block({}) from peer {}", block_id, from_peer);
                        if let Err(e) = bc_manager.lock().await.process_block(&block).await {
                            error!("Process block({}) error: {}", block_id, e);
                        }
                    }
                    Err(e) => {
                        error!("Error deserializing block: {}", e);
                    }
                }
            } else if bc_manager.lock().await.is_tx_topic(&topic).await {
                let tx = Transaction::deserialize(&data);
                match tx {
                    Ok(tx) => {
                        let tx_id = tx.calc_tx_id();
                        info!("Received transaction ({}) from peer {}", tx_id, from_peer);
                        if let Err(e) = bc_manager
                            .lock()
                            .await
                            .process_transaction(tx_id, tx, data)
                            .await
                        {
                            error!("Process transaction ({}) error: {}", tx_id, e);
                        }
                    }
                    Err(e) => {
                        error!("Error deserializing transaction: {}", e);
                    }
                }
            } else {
                error!(
                    "Received a message about a non-block, non-transaction topic: {}",
                    topic
                );
            }
        }

        // Reply with the content of the file on incoming requests.
        P2PEventForApp::InboundRequest {
            requested_content,
            channel,
        } => {
            let block_id = BlockId::new(requested_content.as_bytes());
            let block = bc_manager.lock().await.read_block_and_txs(&block_id).await;

            match block {
                Ok(block) => {
                    info!("Received request for block({})", block_id);
                    bc_manager
                        .lock()
                        .await
                        .respond_content(Ok(block), channel)
                        .await;
                }
                Err(e) => {
                    warn!(
                        "Received request for block({}) but get block data failed({})",
                        block_id,
                        e.to_string()
                    );
                    bc_manager
                        .lock()
                        .await
                        .respond_content(Err(e.to_string()), channel)
                        .await;
                }
            }
        }
    }
}
